/******************************************************************************
KenoGuts.cpp: implementation of the KenoGuts class.

  Copyright 2005 Realtime Gaming.  All rights reserved.
******************************************************************************/
#include "KenoGuts.h"
#include "kenoserv.h"

#include "KenoBet.h"
#include "KenoCard.h"
#include "KenoGame.h"
#include "KenoPayout.h"
#include "KenoSql.h"

#include "log.h"
#include "opts.h"

#include "GameConnection.h"

/******************************************************************************
Static Initialization
******************************************************************************/
const uint8 KenoGuts::MaxCards = 1;
const uint8 KenoGuts::MaxGames = 10;

/******************************************************************************
Construction/Destruction
******************************************************************************/

KenoGuts::KenoGuts(GameConnection* connection)
    : super(connection)
{
    m_game = NULL;

    m_tracker = new KenoSQL( g_tracker );
    m_paytable = new KenoPayout();

	uint8 ipaytable = trackGetPayTable();
	m_paytable->SetPayTable(ipaytable);
}

KenoGuts::~KenoGuts()
{
    delete m_tracker;
    delete m_paytable;
}

/******************************************************************************
Methods
******************************************************************************/
int32   
KenoGuts::getMinBet() const
{
    return MinBet;
}

int32   
KenoGuts::getMaxBet() const
{
    return MaxBet;
}

uint8   
KenoGuts::getMaxCards() const
{
    return MaxCards;
}

uint8   
KenoGuts::getMaxGames() const
{
    return MaxGames;
}

uint8
KenoGuts::getPaytable() const
{ 
    //Add the paytable index to use.
    return (uint8)m_paytable->GetPayTable();
}

int32
KenoGuts::getDenomValue(int32 index) const
{ 
	if(index < 0 || index > 4)
		return 0;
	else
		return m_anDenomValues[index];
}

bool8   
KenoGuts::isValidDenom(int32 denom) const
{
	return (m_anDenomValues[0] == denom || m_anDenomValues[1] == denom || 
		    m_anDenomValues[2] == denom || m_anDenomValues[3] == denom || m_anDenomValues[4] == denom);
}

void    
KenoGuts::processBet(uint8* buf, 
                     int32 len, int32 num_cards, 
                     int32 num_games)
{
	int32 balance, gameCount;
	int32 i = 0;
	uint16 card_cost;
	uint8 num_picks;
	uint8 playerpick[15];
	bool8 validpicks = false;
	GAMERESULT* gr = NULL;


	if(m_game)
	{
        m_connection->respondGameInProgress();
	}

	// get the cost for this card
	card_cost = buf[i] + (buf[i+1] << 8);
	i+=2;

	// get the number of picks for this card
	num_picks = buf[i];
	i++;

	if (num_picks > 0 && num_picks <= 15)
	{
		validpicks = true;
		// and get all the picks.  Lets range check each pick just to make sure
		for(uint8 pick_idx = 0; pick_idx < num_picks; pick_idx++)
		{
			playerpick[pick_idx] = *(buf + i);
			if (playerpick[pick_idx] >= MAX_PICK)
				validpicks = false;
			i++;
		}
	}

	// sanity check the values from the client Bet message
	if (num_picks > 0 && num_picks <= 15 && validpicks && isValidDenom(card_cost))	// CSN-11856: check if card_cost is a correct denomination
	{
		gr = new GAMERESULT[num_games];
		m_connection->trackGetBalance(&balance);

 		for (gameCount=0; gameCount<num_games; ++gameCount)
		{
			KenoCard card;

			card.SetCost(card_cost);

			// Add the picks to the card
			for(uint8 pick_idx = 0; pick_idx < num_picks; pick_idx++)
			{
				card.AddPick(playerpick[pick_idx ]);
			}

			if (!card.IsValid())
			{
				break;
			}
			else
			{
				if (!playGame (card, num_games, gameCount==0, gr[gameCount], balance))
					break;
			}
		}

		if (gameCount > 0)
		{
			startResponse();
			addToResponse((uint8)'B');

			// Loop through each game
			for (i=0; i<gameCount; ++i)
			{
				// Copy the server picks and the pauout for the current game
				for(int32 j = 0; j < MAX_SERVER_PICKS; ++j)
					addToResponse((uint8)gr[i].m_picks[j]);
				addToResponse(gr[i].m_payout);
			}
			addToResponse(balance);
			endResponse();
			gameDone();
		}
		else
		{
			if (balance < 0)
			{
                m_connection->respondInvalid();
			}
            else if ( balance == 0 )
            {
                m_connection->respondNoBalance();
            }
			else
			{
                m_connection->respondBadCard(len);
			}
		}

		delete []gr;

	}
	else
	{
        m_connection->respondBadCard(len);
	}
}

bool8 KenoGuts::playGame (KenoCard& card, int32 numgames, bool8 newGroup, GAMERESULT& gresult, int32& balance)
{
	KenoBet *bet = NULL;
	int32 picks[MAX_SERVER_PICKS];
	int32 payout = 0;
	bool8 success = false;

	bet = new KenoBet(1);
	bet->PlayCard(card);

	if( !m_connection->trackGetBalance(&balance) )
	{
		balance = -1;	// SQL error --> this ensures, we send 'N' message
		return false;
	}

	if( balance > 0 && balance >= bet->GetTotalBet() &&
		bet->GetTotalBet() >= MinBet && bet->GetTotalBet() <= MaxBet)
	{
		m_game = new KenoGame(m_paytable);
		m_game->PlaceBet(bet);

#ifdef FORCED_KENO
		if (ServerOpts::bAllowTesting && m_forcedResults)
		{
			m_game->setForcedResults(m_forcedResults);
		}
#endif

		// balance is adjusted inside of trackNewGame
		if (m_game && trackNewGame(bet, &balance, numgames, newGroup))
		{
			// this is where the deal was...
			if(!m_game->PlayGame(bet, &payout, picks))
			{
				m_connection->trackAdjustBalance(-(bet->GetTotalBet()));
			}
			else
			{
				success = true;
				balance += payout;
				trackGameDone(payout, &balance, picks);
			}
		}

		delete m_game;
		m_game = NULL;
	}
	else
		balance=0;	// this ensures that we send '$' message to the client!!

	// If we got a good game then lets copy the results
	if (success)
	{
		for (int32 i=0; i<MAX_SERVER_PICKS; ++i)
			gresult.m_picks[i] = (uint8)picks[i];
		gresult.m_payout = payout;
	}

	delete bet;
	bet = NULL;
	return (success);
}

void KenoGuts::sendSavedGame()
{
    ASSERT(!"Keno Has no saved games!  Should not have called this method.");
}

void
KenoGuts::gameDone()
{
	delete m_game;
	m_game = (KenoGame *)0;
}

int32   
KenoGuts::getPlayerLimits(const PID& pid, int32* compFactor)
{
	int32 result = TrackSQL::ExecFailed;
    if ( m_tracker )
    {
        result = m_tracker->getPlayerLimits( pid, getGameID(), getMachineID(), compFactor );
    }
    return result;
}

int32   
KenoGuts::getDenominations()
{
	int32 result = TrackSQL::ExecFailed;
    if ( m_tracker )
    {
		result = m_tracker->getDenominations(m_anDenomValues);
    }

	if(result == TrackSQL::NoError)
	{
		MinBet = m_anDenomValues[0];
		MaxBet = m_anDenomValues[4];
	}

    return result;
}

/******************************************************************************
Game-specific Tracker methods
******************************************************************************/
bool8
KenoGuts::trackNewGame(KenoBet *bet, int32 *Balance, 
                       int32 numGames, bool8 newGroup) const
{
	pInfo info = m_connection->getInfo();
	int32 result = TrackSQL::ExecFailed;
	int32 comp;

	if (m_tracker)
	{
		m_tracker->lock();
		result = m_tracker->createGame(ServerOpts::casinoId,
			info->pid, info->forMoney, info->sessionId,
			info->forMoney ? m_compFactor : 0,
			info->gid, Balance, &comp,
			bet, numGames, newGroup);
		m_tracker->unlock();
	}

	return (bool8)(result == TrackSQL::NoError);
}

uint8
KenoGuts::trackGetPayTable() const
{
	int32 result = TrackSQL::ExecFailed;
	uint8 paytable=0;

	if (m_tracker)
	{
		m_tracker->lock();
		result = m_tracker->getPayTable(&paytable);
		m_tracker->unlock();
	}

	if (result == TrackSQL::NoError)
		return paytable;

	return 0;
}

bool8
KenoGuts::trackGameDone(int32 BalAdjust, int32* Balance, int32 *results) const
{
	pInfo info = m_connection->getInfo();
	int32 result = TrackSQL::ExecFailed;
	int32 comp;

	if (m_tracker && info)
	{
		m_tracker->lock();
		result = m_tracker->gameDone(ServerOpts::casinoId,
			info->pid, info->forMoney, info->gid,
			BalAdjust, Balance, &comp, results);
		m_tracker->unlock();
	}

	return (bool8)(result == TrackSQL::NoError);
}

#ifdef FORCED_KENO
void KenoGuts::loadForcedResults()
{
	pInfo info  = m_connection->getInfo();
	int32 sqlResult = TrackSQL::ExecFailed;
	String results;

	// Get forced results as a string from DB
	if (m_tracker && info)
	{
		m_tracker->lock();
		sqlResult = m_tracker->getForcedResults(info->pid, GAMEID, results);
		m_tracker->unlock();
	}
	
	// Load results into a GameForcedResults object
	if (sqlResult == TrackSQL::NoError)
	{
		if (!results.isEmpty())
		{
			if (!m_forcedResults)
				m_forcedResults = new GameForcedResults();

			m_forcedResults->load(results);
		}
	}
	else
	{
		Log::msgError("Forced results couldn't be loaded for pid=%S", (const wchar_t*)info->pid);
	}
}
#endif
